package com.ywk.effective._11_clone;


public class HashTable<K, V> implements Cloneable {

    private Entry[] buckets;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    private transient int count;

    public HashTable() {
        this.buckets = new Entry[DEFAULT_INITIAL_CAPACITY];
    }

    static class Entry<K, V> implements Cloneable {
        int hash;
        K key;
        V value;
        Entry<K, V> next;

        public Entry(int hash, K key, V value, Entry next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        @Override
        protected Entry clone() throws CloneNotSupportedException {
         /*   Entry entry = (Entry) super.clone();
            if (entry.next != null) {
                return entry.next.clone();
            }
            return entry;*/
            Entry result = new Entry(hash, key, value, next);
            for (Entry p = result; p.next != null; p = p.next) {

                p.next = new Entry(p.next.hash, p.next.key, p.next.value, p.next.next);
            }
            return result;
        }
    }


    public Object get(Object key) {
        Entry tab[] = buckets;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry e = tab[index]; e != null; e = e.next) {
            if ((e.hash == hash) && (e.key.equals(key))) {
                return e.value;
            }
        }
        return null;
    }

    public Object put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry tab[] = buckets;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry entry = (Entry) tab[index];
        for (; entry != null; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                Object old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }

    private void addEntry(int hash, K key, V value, int index) {
        size++;

        Entry tab[] = buckets;
        if (count >= DEFAULT_INITIAL_CAPACITY) {
            // Rehash the table if the threshold is exceeded
            tab = buckets;
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        // Creates the new entry.
        @SuppressWarnings("unchecked")
        Entry e = (Entry) tab[index];
        tab[index] = new Entry(hash, key, value, e);
        count++;
    }

    @Override
    protected HashTable clone() throws CloneNotSupportedException {
        HashTable result = (HashTable) super.clone();
        result.buckets = new Entry[buckets.length];
        for (int i = 0; i < buckets.length; i++) {
            if (buckets[i] != null) {
                result.buckets[i] = buckets[i].clone();
            }

        }
        return result;
    }

    static class Main {
        public static void main(String[] args) throws CloneNotSupportedException {

            HashTable hashtable = new HashTable();
            hashtable.put("String", "haha");
            hashtable.put("Str", "ha");
            HashTable o = (HashTable) hashtable.clone();
            o.put("String", "we");
            System.out.println(hashtable.get("String"));

        }
    }
}
